﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Heijden.DNS;

namespace SMTP.Test.Client.Requests
{
    public class DNSResponse : EventArgs
    {
        public string Response { get; private set; }

        public DNSResponse(string msg)
        {
            Response = msg;
        }
    }

    class DNSRecordTest
    {
        public event EventHandler<DNSResponse> DNSResponseMessage;
        public event EventHandler DNSQueryComplete;

        public QType QueryType { get; set; }
        public QClass QueryClass { get; set; }
        public string Server { get; set; }

        public DNSRecordTest()
        {
            Server = string.Empty;
            QueryType = QType.A;
            QueryClass = QClass.ANY;
        }

        public DNSRecordTest(string server, QType queryType = QType.A, QClass queryClass = QClass.ANY)
        {
            Server = server;
            QueryType = queryType;
            QueryClass = queryClass;
        }

        public void QueryDNS()
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    //this will set our host dns server 
                    Resolver _resolver = new Resolver();
                    _resolver.Recursion = true;
                    _resolver.UseCache = false;
                    _resolver.DnsServer = _resolver.DnsServers[0].Address.ToString();
                    _resolver.TransportType = TransportType.Udp;

                    OnDNSResponseMessage(string.Format("; <<>> Dig.Net {0} <<>> @{1} {2} {3}", _resolver.Version, _resolver.DnsServer, QueryType, Server));
                    OnDNSResponseMessage(";; global options: printcmd");

                    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

                    sw.Start();
                    Response _response = _resolver.Query(Server, QueryType, QueryClass);
                    sw.Stop();

                    if (string.IsNullOrEmpty(_response.Error))
                    {
                        OnDNSResponseMessage(String.Format(";; ->>HEADER<<- opcode: {0}, status: {1}, id: {2}",
                            _response.header.OPCODE,
                            _response.header.RCODE,
                            _response.header.ID));
                        OnDNSResponseMessage(String.Format(";; flags: {0}{1}{2}{3}; QUERY: {4}, ANSWER: {5}, AUTHORITY: {6}, ADDITIONAL: {7}",
                            _response.header.QR ? " qr" : "",
                            _response.header.AA ? " aa" : "",
                            _response.header.RD ? " rd" : "",
                            _response.header.RA ? " ra" : "",
                            _response.header.QDCOUNT,
                            _response.header.ANCOUNT,
                            _response.header.NSCOUNT,
                            _response.header.ARCOUNT));

                        OnDNSResponseMessage("");

                        if (_response.header.QDCOUNT > 0)
                        {
                            OnDNSResponseMessage(";; QUESTIONS SECTION:");
                            foreach (var q in _response.Questions)
                            {
                                OnDNSResponseMessage(";" + q);
                            }
                            OnDNSResponseMessage("");
                        }

                        if (_response.header.ANCOUNT > 0)
                        {
                            OnDNSResponseMessage(";; ANSWER SECTION:");
                            foreach (var answerRR in _response.Answers)
                            {
                                OnDNSResponseMessage(answerRR.ToString());
                            }
                            OnDNSResponseMessage("");
                        }

                        if (_response.header.NSCOUNT > 0)
                        {
                            OnDNSResponseMessage(";; AUTHORITY SECTION:");
                            foreach (var authorityRR in _response.Authorities)
                            {
                                OnDNSResponseMessage(authorityRR.ToString());
                            }
                            OnDNSResponseMessage("");
                        }

                        if (_response.header.ARCOUNT > 0)
                        {
                            OnDNSResponseMessage(";; ADDITIONAL SECTION:");
                            foreach (var additionalRR in _response.Additionals)
                            {
                                OnDNSResponseMessage(additionalRR.ToString());
                            }
                            OnDNSResponseMessage("");
                        }

                        OnDNSResponseMessage(String.Format(";; Query time: {0} msec", sw.ElapsedMilliseconds));
                        OnDNSResponseMessage(String.Format(";; SERVER: {0}#{1}({2})", _response.Server.Address, _response.Server.Port, _response.Server.Address));
                        OnDNSResponseMessage(";; WHEN: " + _response.TimeStamp.ToString("ddd MMM dd HH:mm:ss yyyy", new System.Globalization.CultureInfo("en-US")));
                        OnDNSResponseMessage(";; MSG SIZE rcvd: " + _response.MessageSize);
                    }
                    else
                    {
                        //error occurred
                        OnDNSResponseMessage(";; " + _response.Error);
                    }

                }
                catch (Exception ex)
                {
                    string msg = ex.Message;
                    Exception innerException = ex.InnerException;

                    while (innerException != null)
                    {
                        if (!string.IsNullOrEmpty(innerException.Message))
                        {
                            msg += innerException.Message + "\r\n";
                        }
                        innerException = innerException.InnerException;
                    }

                    OnDNSResponseMessage("An error occured: " + msg);
                }

                OnDNSQueryComplete();
            });
        }

        private void OnDNSResponseMessage(string msg)
        {
            var handler = DNSResponseMessage;
            if (handler != null) DNSResponseMessage(this, new DNSResponse(msg));
        }

        private void OnDNSQueryComplete()
        {
            var handler = DNSQueryComplete;
            if (handler != null) DNSQueryComplete(this, EventArgs.Empty);
        }
    }
}
